
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfoList>
#include <QMessageBox>
#include <QProcessEnvironment>
#include <QRegExp>
#include <QScriptValue>
#include <QScriptEngine>
#include <QScriptValueIterator>

#include "settings.h"

#define DEFAULT_PROXY_PORT 0

Settings::Settings()
{
	m_settings = new QSettings("Nokia", "Hybrid Application Generator");
	m_overridingSettings = new QMap<Settings::Parameter, QVariant>;
}

Settings::~Settings()
{
	delete m_settings;
	delete m_overridingSettings;
}

/**
 * Returns a QVariant configuration parameter associated with the given argument.
 * If no value is saved yet, a default value will be returned instead.
 */
QVariant Settings::get(Parameter  key,
                       QString    app)
{
	// Check if the key is found in the temporary settings
	if (instance().m_overridingSettings->contains(key))
	{
		return instance().m_overridingSettings->value(key);
	}

    QString settingKey = getKey(key,
                                app);
	QVariant value;

	if (key == ShowFullScreen)
	{
		// Special cases not saved in QSettings
		LayoutMode mode;
		mode = (LayoutMode) get(WidgetLayout).toInt();
		if (mode == FullScreenWithSoftKeys || mode == FullScreenWithoutSoftKeys)
			return true;
		else
			return false;
	}
	else if (key == ShowSoftKeys)
	{
		// Special cases not saved in QSettings
		LayoutMode mode;
		mode = (LayoutMode) get(WidgetLayout).toInt();
		if (mode == FullScreenWithoutSoftKeys)
			return false;
		else
			return true;
	}
	else if (key == QtVersionString)
	{
		// Check version. Recognizes style "Using Qt version 4.6.2"
		QString version = "<not detected>";
		QProcess process;
		process.start("cmd /C \"" +  get(Settings::QtEnvBatPath).toString() + " & qmake --version\"");
		process.waitForFinished();
		QString output = process.readAllStandardOutput();
		QRegExp versionRx;
		versionRx.setPattern("^.*([0-9].[0-9].[0-9])");
		if (versionRx.indexIn(output) != -1)
		{
			version = versionRx.cap(1);
		}
		return version;
	}
	else if (key == QtVersion)
	{
		// Check version. Recognizes style "Using Qt version 4.6.2"
		int version = -1;
		QRegExp versionRx;
		versionRx.setPattern("^.*([0-9]).([0-9]).([0-9])");
		if (versionRx.indexIn(get(QtVersionString).toString()) != -1)
		{
			version = versionRx.cap(1).toInt() * 100 +
					  versionRx.cap(2).toInt() * 10 +
					  versionRx.cap(3).toInt();
		}
		return version;
	}
	else
	{
		// Normal settings, saved in QSettings
		if (instance().getSettingObject()->contains(settingKey))
		{
			// Already saved in settings, just return
            return instance().getSettingObject()->value(getKey(key, app));
		}
		else
		{
            // Provide appropriate default values
            switch (key) {
            case UID3:
            case LastWidgetPath:
            case LastDirectoryPath:
                value = "";
                break;
            case FullS60SDK:
                value = false;
				break;
            case PanningEnabled:
                value = false;
                break;
            case TextSelectionEnabled:
                value = true;
                break;
            case WidgetLayout:
                value = 0;
                break;
            case LogFilePath:
                value = QDir::toNativeSeparators(QDir::currentPath() + "/" + GENERATOR_LOG_FILE);
                break;
            case QtEnvBatPath:
                value = instance().guessQtEnvPath();
                break;
            case MaddePath:
                value = "";
                break;
            case HybridAuthor:
                value = "";
                break;
            case HybridEmail:
                value = "";
                break;
            case Licence:
                value = "lgpl";
                break;
            case HybridShortDesc:
                value = "Hybrid App (short desc)";
                break;
            case HybridLongDesc:
                value = "Hybrid App (long desc)";
                break;
            case ForumNokiaUserName:
                value = "";
                break;
            case ProxyConfig:
                value = PCK_UndefinedProxyConfig;
                break;
            case ProxyUrl:
                value = "";
                break;
            case ProxyPort:
                value = DEFAULT_PROXY_PORT;
                break;
            case ProxyUserName:
                value = "";
                break;
            case ProxyPassword:
                value = "";
                break;
            case RcProperties:
                value = "";
                break;
            default:
                value = false;
                break;
            }

            instance().set(key, value, app);
            return value;
		}
	}
}

/**
 * Saves the given parameter with the provided value.
 */
void Settings::set(Parameter   key,
                   QVariant    value,
                   QString     app)
{
    if ((key == HybridShortDesc || key == HybridLongDesc)
        && app.length() == 0)
    {
        // these settings are supported to be app specific,
        // not saving them
        return;
    }

    instance().getSettingObject()->setValue(getKey(key, app), value);
}

/**
 * Saves the given parameter with the provided value as a temporary
 * setting -- i.e. the setting will not be saved to the permanent
 * QSetting configuration.
 */
void Settings::setTemporary(Parameter key, QVariant value)
{
	if (instance().m_overridingSettings->contains(key))
	{
		instance().m_overridingSettings->remove(key);
	}
	instance().m_overridingSettings->insert(key, value);
}

/**
 * Returns QSettings object that is used to actually store
 * the configuration.
 */
QSettings* Settings::getSettingObject()
{
	return m_settings;
}

/**
 * Return a QString associated with the given key. The QString
 * will be used as a key to access the QSettings value.
 */
QString Settings::getKey(Parameter        key,
                         const QString  & app)
{
    switch (key) {
    case QtEnvBatPath:
        return "Environment/QtEnvBatPath";
    case FullS60SDK:
        return "Environment/FullS60SDK";
    case LogFilePath:
        return "Environment/LogFilePath";
    case LastWidgetPath:
        return "Environment/LastWidgetPath";
    case LastDirectoryPath:
        return "Environment/LastDirectoryPath";
    case UID3:
        return "Project/UID3";
    case PanningEnabled:
        return "Project/PanningEnabled";
    case TextSelectionEnabled:
        return "Project/TextSelectionEnabled";
    case WidgetLayout:
        return "Project/WidgetLayout";
    case MaddePath:
        return "Environment/MaddePath";
    case HybridAuthor:
        return "Project/HybridAuthor";
    case HybridEmail:
        return "Project/HybridEmail";
    case Licence:
        return "Project/Licence";
    case HybridShortDesc:
        return QString("Project/%1/HybridShortDesc").arg(app);
    case HybridLongDesc:
        return QString("Project/%1/HybridLongDesc").arg(app);
    case ForumNokiaUserName:
        return "Environment/Network/ForumNokiaUserName";
    case ProxyConfig:
        return "Environment/Network/ProxyConfig";
    case ProxyUrl:
        return "Environment/Network/ProxyUrl";
    case ProxyPort:
        return "Environment/Network/ProxyPort";
    case ProxyUserName:
        return "Environment/Network/ProxyUserName";
    case ProxyPassword:
        return "Environment/Network/ProxyPassword";
    case RcProperties:
        return "RemoteCompiler/Properties";
    default:
        return "Default";
    }
}

/**
 * Tries to guess the path to qtenv.bat. First checks the QT_ENV_BAT environmental
 * variable, and if the file given by it exists, the value will be used. If no such
 * env variable exists, tries to use Qt path from PATH to find the bat file. Gives
 * up if still no hit.
 */
QString Settings::guessQtEnvPath() const
{
#ifdef Q_OS_WIN32
    // First, guess Nokia Qt SDK path from registry if we're on Windows
    // This would be the preferred development environment
    QSettings installation("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nokia Qt SDK", QSettings::NativeFormat);
    QString location = installation.value("InstallLocation").toString();
    if (!location.isEmpty())
    {
        location += "\\Symbian\\SDK\\bin\\qtenv2.bat";
        if (QFile::exists(location))
        {
            // Found Nokia Qt SDK's qtenv2.bat!
            return QDir::toNativeSeparators(location);
        }
    }
#endif

    QString path = "";

	// Settings do not contain env bat. Check the env for QT_ENV_BAT, and
	// then for Qt path, and use either if found. Leave empty otherwise.
	QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
	if (environment.contains("QT_ENV_BAT") && QFile::exists(environment.value("QT_ENV_BAT")))
	{
		// Nice, the qtenv.bat was found straight from the env
		path = environment.value("QT_ENV_BAT");
	}
	else
	{
		// Look for Qt directories in PATH, check them for .bat
		QRegExp rx("([^;]*Qt[^;]*bin)");
		int pos = 0;
		while ((pos = rx.indexIn(environment.value("PATH"), pos)) != -1)
		{
			QString envTest = rx.cap(1) + "\\qtenv.bat";
			if (QFile::exists(envTest))
			{
				path = QDir::toNativeSeparators(envTest);
				break;
			}
			pos += rx.matchedLength();
		}
	}

	return path;
}


namespace
{
    QScriptValue GetRcpScriptValue(QScriptEngine & scriptEngine)
    {
        QScriptValue
            rv;

        QString
            rcp(Settings::get(Settings::RcProperties).toString());

        if (rcp.length() > 0)
        {
            rcp.prepend("(");
            rcp.append(")");
            rv = scriptEngine.evaluate(rcp);
            if (scriptEngine.hasUncaughtException()) {
                rv = QScriptValue();
            }
        }

        return rv;
    }
}


Settings::OsDescMap  Settings::RcOSes(QScriptEngine & scriptEngine)
{
    OsDescMap
        rv;

    QScriptValue
        rcpsv(GetRcpScriptValue(scriptEngine));

    if (!rcpsv.isUndefined())
    {
        QScriptValue
            titles = rcpsv.property("titles").property("os");
        QScriptValueIterator
            it(titles);
        while (it.hasNext())
        {
            it.next();
            /* OBS
            QPair<QString, QString>
                ent;
                */
            QString
                os = it.name();
            QString
                desc = it.value().toString();
            /*
            ent.first = os;
            ent.second = desc;
            rv.append(ent);
            */
            rv.insert(os,
                      desc);
        }
    }

    return rv;
}


Settings::OsQtPairList Settings::RcOsQtPairs(QScriptEngine & scriptEngine)
{
    OsQtPairList
        rv;

    QScriptValue
        rcpsv(GetRcpScriptValue(scriptEngine));

    if (!rcpsv.isUndefined())
    {
        QScriptValue
            titles = rcpsv.property("titles").property("os");
        QScriptValue
            maps = rcpsv.property("map").property("os");
        QScriptValueIterator
            it(maps);
        while (it.hasNext())
        {
            it.next();
            QString
                os = it.name();
            QScriptValueIterator
                qtit(it.value().property("qt"));
            while (qtit.hasNext()) {
                qtit.next();
                QString
                    qt = qtit.name();
            /*
            ent.first = os;
            ent.second = QString("pSet[os]=%1&pSet[qt]=%2").arg(os, qt);
            rv.append(ent);
            */
                if (os == "maemo5" || qt != "4_6_2")
                    rv.append(OsQtPair(os, qt));
            }
        }
    }

    return rv;
}


Settings::OsQtPair::OsQtPair(QString os,
                   QString qt)
    : m_os(os)
    , m_qt(qt)
{
    ;
}


QString Settings::OsQtPair::wccParams() const
{
    return QString("pSet[os]=%1&pSet[qt]=%2").arg(m_os, m_qt);
}


bool Settings::IsRcConfigured()
{
    bool
        rv = false;

    QString
        forumNokiaUserName = get(ForumNokiaUserName).toString();

    if (forumNokiaUserName.length() > 0)
    {
        ProxyConfigKind
            proxyConfig = static_cast<ProxyConfigKind>(get(ProxyConfig).toInt());

        if (proxyConfig == PCK_ManualProxy)
        {
            QString
                url = get(ProxyUrl).toString(),
                userName = get(ProxyUserName).toString(),
                password = get(ProxyPassword).toString();
            int
                port = get(ProxyPort).toInt();
            rv = url.length() > 0
                 && userName.length() > 0
                 && password.length() > 0
                 && port != DEFAULT_PROXY_PORT;
        }
        else
        {
            rv = proxyConfig != Settings::PCK_UndefinedProxyConfig;
        }
    }

    return rv;
}
